﻿using System;
using UnityEngine;

namespace Miao.Unity
{
    /// <summary>
    /// Provides utility functions for vector.
    /// </summary>
    public static class VectorUtility
    {
        /// <summary>
        /// Get the quadrant of direction <paramref name="direction"/>.
        /// </summary>
        public static QuadrantType Quadrant(Vector2 direction)
        {
            if (direction.x >= 0f) return direction.y >= 0f ? QuadrantType.TopRight : QuadrantType.BottomRight;
            else return direction.y >= 0f ? QuadrantType.TopLeft : QuadrantType.BottomLeft;
        }
        /// <summary>
        /// 获取垂直于方向 <paramref name="direction"/> 的两个方向
        /// </summary>
        /// <param name="safe">如果为 false, 则对 <paramref name="direction"/> 进行归一化处理 </param>
        public static void VerticalDirection(Vector2 direction, out Vector2 leftDirection, out Vector2 rightDirection, bool safe = false)
        {
            VerticalDirection(direction, out leftDirection, out rightDirection, CoordinateType.Standard, safe);
        }
        /// <summary>
        /// 获取垂直于方向 <paramref name="direction"/> 的两个方向
        /// </summary>
        /// <param name="safe">如果为 false, 则对 <paramref name="direction"/> 进行归一化处理 </param>
        public static void VerticalDirection(Vector2 direction, out Vector2 leftDirection, out Vector2 rightDirection, CoordinateType coordinateType, bool safe = false)
        {
            if (!safe) direction.Normalize();
            //由二维旋转矩阵化简而来
            //相当于旋转向量90°和-90°
            switch (coordinateType)
            {
                case CoordinateType.View:
                    leftDirection = new Vector2(direction.y, -direction.x);
                    rightDirection = new Vector2(-direction.y, direction.x);
                    return;
                case CoordinateType.Standard:
                    leftDirection = new Vector2(-direction.y, direction.x);
                    rightDirection = new Vector2(direction.y, -direction.x);
                    return;
                default: throw new Exception("不可达分支");
            }
        }
        /// <summary>
        /// 获取位于左侧的垂直方向
        /// </summary>
        /// <param name="safe">如果为 false, 则对 <paramref name="direction"/> 进行归一化处理 </param>
        public static Vector2 VerticalDirectionLeft(Vector2 direction, bool safe = false)
        {
            return VerticalDirectionLeft(direction, CoordinateType.Standard, safe);
        }
        /// <summary>
        /// 获取位于左侧的垂直方向
        /// </summary>
        /// <param name="safe">如果为 false, 则对 <paramref name="direction"/> 进行归一化处理 </param>
        public static Vector2 VerticalDirectionLeft(Vector2 direction, CoordinateType coordinateType, bool safe = false)
        {
            if (!safe) direction.Normalize();
            return coordinateType switch
            {
                CoordinateType.View => new Vector2(direction.y, -direction.x),
                CoordinateType.Standard => new Vector2(-direction.y, direction.x),
                _ => throw new Exception("不可达分支"),
            };
        }
        /// <summary>
        /// 获取位于右侧的垂直方向
        /// </summary>
        /// <param name="safe">如果为 false, 则对 <paramref name="direction"/> 进行归一化处理 </param>
        public static Vector2 VerticalDirectionRight(Vector2 direction, bool safe = false)
        {
            return VerticalDirectionRight(direction, CoordinateType.Standard, safe);
        }
        /// <summary>
        /// 获取位于右侧的垂直方向
        /// </summary>
        /// <param name="safe">如果为 false, 则对 <paramref name="direction"/> 进行归一化处理 </param>
        public static Vector2 VerticalDirectionRight(Vector2 direction, CoordinateType coordinateType, bool safe = false)
        {
            if (!safe) direction.Normalize();
            return coordinateType switch
            {
                CoordinateType.View => new Vector2(-direction.y, direction.x),
                CoordinateType.Standard => new Vector2(direction.y, -direction.x),
                _ => throw new Exception("不可达分支"),
            };
        }

        /// <summary>
        /// 获取向量 <paramref name="vector3"/> 在平面上的投影向量
        /// </summary>
        public static Vector3 ProjectOnPlane(Vector3 vector3, Vector3 planeNormal)
        {
            return Vector3.ProjectOnPlane(vector3, planeNormal);
        }
        /// <summary>
        /// 获取向量 <paramref name="direction"/> 在平面上的投影方向
        /// </summary>
        public static Vector3 ProjectOnPlaneDirection(Vector3 direction, Vector3 planeNormal)
        {
            return Vector3.ProjectOnPlane(direction, planeNormal).normalized;
        }
        /// <summary>
        /// 以指定的角度 <paramref name="angle"/> 旋转向量 <paramref name="vector3"/>
        /// </summary>
        public static Vector3 RotateVector3(Vector3 angle, Vector3 vector3)
        {
            return Quaternion.Euler(angle) * vector3;
        }

        /// <summary>
        /// <see cref="Vector2"/> 叉乘.
        /// </summary>
        public static float Cross(Vector2 a, Vector2 b)
        {
            //值为正,则b在a的逆时针方向;
            //值为负,则b在a的顺时针方向;
            //值为0, 则两者共线;
            return a.x * b.y - a.y * b.x;
        }
        /// <summary>
        /// 点乘.
        /// </summary>
        public static float Dot(Vector2 a, Vector2 b)
        {
            return a.x * b.x + a.y * b.y;
        }

        /// <summary>
        /// Converts an unsigned angle <paramref name="angle"/> to its corresponding clockwise direction.
        /// </summary>
        public static Vector2 AngleToDirection(float angle)
        {
            return new Vector2(MathW.SinDeg(angle), MathW.CosDeg(angle));
        }

        /// <summary>
        /// 获取方向 <paramref name="direction"/> 对应的角度.
        /// 角度即该向量与向量(0, 1)的夹角.
        /// </summary>
        public static float DirectionToAngle(Vector2 direction, AngleRangeType angleRangeType = AngleRangeType.SignedHalfRange)
        {
            return Angle(Vector2.up, direction, angleRangeType);
        }
        /// <summary>
        /// 获取向量 <paramref name="start"/> 和 <paramref name="end"/> 之间的夹角.
        /// 这个夹角以<paramref name="start"/> 为起始轴.
        /// </summary>
        public static float Angle(Vector2 start, Vector2 end, AngleRangeType angleRangeType = AngleRangeType.SignedHalfRange)
        {
            //平面向量夹角公式 Angle = Acos(Dot(a, b)/(|a||b|)) * (180 / pi)
            //即该夹角的 cos = a和b的点积除以 a和b的长度的乘积, 然后再反余弦后乘以 180 / pi 即可得到夹角
            float product = MathW.Sqrt(start.sqrMagnitude * end.sqrMagnitude);
            if (product < 0.000001f) return 0f;
            float cos = Math.Clamp(Dot(start, end) / product, -1f, 1f);// dot / product 由于精度问题可能会超出1, 必须进行约束
            return angleRangeType switch
            {
                AngleRangeType.HalfRange => MathW.Acos(cos) * MathW.rad2deg,
                AngleRangeType.SignedHalfRange => MathW.Acos(cos) * MathW.rad2deg * Math.Sign(Cross(start, end)),
                AngleRangeType.FullRange => AngleUtility.SignedAngleToFullAngle(MathW.Acos(cos) * MathW.rad2deg * Math.Sign(Cross(start, end))),
                _ => throw new Exception("不可达分支"),
            };
        }
    }
}